<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="stylesheet" href="./style.css">
    <title>Document</title>
    <script src="./vue.js"></script>
</head>
<body>
    <!-- 需求分析：
    1.每个标签页的主体内容（即html结构、文本、图片等）应当是由使用组件的父级控制的，这部分可以作为一个slot，
    slot的数量决定了标签切换按钮的数量。点击每个按钮时，另外的标签对应的slot应该被隐藏掉。第一种想法，在slot里写三个div，再
    根据需要隐藏和显示。更好的办法应该是让组件帮忙处理这部分逻辑，我们只需要聚焦slot内容本身。这种情况下，再定义一个子组件pane，
    嵌套在标签页组件tabs里，把业务代码放在pane的slot内，然后各个pane都作为整个tabs的组件。
    
        tabs和pane两个组件是分离的，但tabs上的标题应该由pane组件来定义，又因为slot写在pane里，所以应该在组件初始化（或标签标题动态改变）时，tabs从
        当前的pane里获取标题并保存起来。-->
    
    <div id="app" v-cloak>
        <tabs v-model="activeKey">
            <pane label="标签一" name="1">
                标签一内容
            </pane>
            <pane label="标签二" name="2">
                标签二内容
            </pane>
            <pane label="标签三" name="3">
                标签三内容
            </pane>
        </tabs>
    </div>
<script>
    Vue.component('pane',{
        name:"pane",
        template:'\
            <div class="pane" v-show="show">\
                <slot></slot>\
            </div>',
            data() {
                return {
                    show:true//pane需要控制标签页内容的显示与隐藏，配合v-show
                    //点击到这个pane对应的标签页按钮时，这个pane的show值才设置为true
                    //既然这样的话就应该有唯一标识的值来标识这个pane，可以设置一个prop：name让用户来设置，但不是必必需的，默认从0开始
                    //这一步操作由pane执行，pane本身并不知道自己是第几个。
                    //还有prop：label，tabs组件需要将它显示在标签栏标题中

                }
            },
            props:{
                name:{
                    type:String
                },
                label:{
                    type:String,
                    default:''
                }
            },//prop:label是用户可以动态调整的，所以在pane初始化、label更新时都需要通知父组件也更新，
            //可以直接通过this.$parent来访问tabs组件的实例来调用它的方法更新标题
            methods: {
                updateNav(){
                    this.$parent.updateNav();//调用tabs的方法updateNav
                }
            },
            watch: {
                label(){
                    this.updateNav();
                }
            },
            mounted() {
                
                this.updateNav();//在pane初始化时调用一遍tabs的方法updateNav，同时监听prop:label，在其更新时也调用
            },
    })


    Vue.component('tabs',{
        template:'\
            <div class="tabs">\
                <div class="tabs-bar">\
                    <!--标签页的标题，需要使用v-for-->\
                    <div \
                        :class="tabCls(item)"\
                        v-for="(item,index) in navList"\
                        @click="handleChange(index)">\
                        {{ item.label }}\
                    </div>\
                </div>\
                <div class="tabs-content">\
                    <!--这里的slot即是嵌套的pane组件-->\
                    <slot></slot>\
                </div>\
            </div>',
            props:{
                value:{
                    type:[String,Number]
                }
            },
            data() {
                return {
                    //由于不能修改value于是自己复制一份维护
                    currentValue:this.value,
                    //用于渲染tabs的标题
                    navList:[]
                }
            },
            methods:{
                tabCls:function(item){
                    return [
                        'tabs-tab',
                        {
                            //给当前选中的div加一个class
                            'tabs-tab-active':item.name === this.currentValue
                        }
                    ]
                },

                //点击tab标题时触发
                handleChange:function(index){
                    var nav = this.navList[index];
                    var name = nav.name;
                    //改变当前选中的tab，并触发下面的watch
                    this.currentValue = name;
                    //更新value
                    this.$emit('input',name);
                    //触发一个自定义事件供父级使用
                    this.$emit('on-click',name);
                },
                getTabs(){
                    
                    //通过遍历子组件得到所有panes组件
                    return this.$children.filter(function(item){
                        return item.$options.name === 'pane'
                    })
                },
                updateNav(){
                    this.navList = [];
                    //设置对this的引用，在function回调里this指向的并不是Vue实例
                    var _this = this;

                    this.getTabs().forEach(function(pane,index){
                        _this.navList.push({
                            label:pane.label,
                            name:pane.name || index
                        });
                        //如果没有给pane设置name就默认设置为索引
                        if(!pane.name){
                            pane.name = index;
                        }//设置当前选中的tabs的索引
                        if(index === 0){
                            if(!_this.currentValue){
                                _this.currentValue = pane.name || index;
                            }
                        }
                    });

                    this.updateStatus();
                },

                updateStatus(){
                    var tabs = this.getTabs();
                    //再次遍历pane组件，不过这是为了将当前选中的
                    //tab对应的pane组件内容显示出来，并将没有选中的隐藏掉
                    //在上一步中可能需要我们来设置currentValue来标识当前选中项的name（只有在用户没有设置value时才会自动设置）
                    var _this = this;
                    //显示当前选中的tab对应的pane组件，隐藏没有选中的
                    tabs.forEach(function(tab){
                        return tab.show = tab.name === _this.currentValue;
                        // return tab.show = (tab.name === _this.currentValue);
                    })
                }
            },
            
            watch:{
                value:function(val){
                    this.currentValue = val;
                },
                currentValue:function(){
                    //在当前选中的tabs发生变化时更新pane显示状态
                    this.updateStatus();
                }
            }
    });
    
    var app = new Vue({
        el:"#app",
        data:{
            activeKey:'1'
        }
    });
    
    
</script>
</body>
</html>